/*********************************************
*函数功能：通过按键控制LED灯
*******************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of.h>

#if 0
/ {
	fsled{
		compatible="4412,fsled";
		myled=<&gpx2 7 1>;
		interrupt-parent = <&gpx1>;
		interrupts = <2 2>;
	};
}
#endif
#define DEVNAME "fsled"
#define KEYNAME "fskey"
static struct cdev *fsled = NULL;
static struct class *cls = NULL;
static struct device *devp = NULL;
static struct device_node *np = NULL;
static int major = 0;
static int baseminor = 0;
static int count = 1;

static int gpio;
static int irq;
static struct work_struct work_queue;
static int i = 0;
void work_func(struct work_struct *work)
{

	mdelay(2);
	i++;
	//printk("key interrupt\n");
	if (i == 1)
	{
		gpio_set_value(gpio, 1);
	}
	if (i == 2)
	{
		i = 0;
	}
	if (i == 0)
	{
		gpio_set_value(gpio, 0);
	}
}

irqreturn_t key_handler(int irq, void *data)
{

	schedule_work(&work_queue);
	return IRQ_HANDLED;
}

static struct file_operations fops = {
	.owner = THIS_MODULE,
};

static int __init mydev_init(void)
{
	//printk(KERN_INFO "%s : %s : %d\n", __FILE__, __func__, __LINE__);
	dev_t dev;
	int err;
	int ret = 0;
	//0 找到设备数节点
	np = of_find_node_by_path("/fskey");   //从设备树中获取设备节点    
	if(np == NULL)
	{
		printk(KERN_ERR "of_find_node_by_path fail\n");
		return -EINVAL;
	}
	//printk("%p\n",np);
	//1 通过设备树得到gpio和中断号
	gpio = of_get_named_gpio(np, "myled", 0);
	if (gpio < 0)
	{
		printk(KERN_ERR "of_get_named_gpio\n");
		return gpio;
	}
	printk(KERN_INFO "%s : %s : %d\n", __FILE__, __func__, __LINE__);

	//2 gpio口设置为输出
//	gpio_direction_output(gpio, 0);
//	gpio_set_value(gpio, 0);
#if 0	
	gpio = devm_gpio_request(devp,gpio,"gpio2-7");
	if(gpio <0)
	{
		printk("devm_gpio_request fail");
		return gpio;
	}
#endif    
	//3 得到中断
	irq = irq_of_parse_and_map(np, 0);
	if (irq < 0)
	{
		printk("request irq fail\n");
		return irq;
	}
	//4 请求中断
	printk("irq:%d\n", irq);
	ret = request_irq(irq, key_handler, IRQF_TRIGGER_FALLING, KEYNAME, NULL);
	if (ret)
	{
		printk("request fail\n");
		return ret;
	}
	//5 分配主设备号*2
	err = alloc_chrdev_region(&dev, baseminor, count, DEVNAME);
	if (err)
	{
		goto ERR_STEP0;
	}
	major = MAJOR(dev);
	//6 分配字符设备结构体*2
	fsled = cdev_alloc();
	if (NULL == fsled)
	{
		err = -ENOMEM;
		goto ERR_STEP;
	}
	//7 初始化字符设备结构体
	cdev_init(fsled, &fops);
	//8 注册设备结构体
	cdev_add(fsled, major, count);
	//9 创建class类
	cls = class_create(THIS_MODULE, DEVNAME);
	if (IS_ERR(cls)) {
		err = PTR_ERR(cls);
		goto ERR_STEP1;
	}
	devp = device_create(cls, NULL, MKDEV(major, 0), NULL,DEVNAME);
	if (IS_ERR(devp)) {
		err = PTR_ERR(devp);
		goto ERR_STEP2;
	}
/*
    devp = device_create(cls, NULL, MKDEV(major, 1), NULL, "fskey");
	if (IS_ERR(devp)) {
		err = PTR_ERR(devp);
		goto ERR_STEP3;
	}
*/
	INIT_WORK(&work_queue, work_func);    //初始化工作对列*******************
	return 0;
//ERR_STEP3:
 //   device_destroy(cls, MKDEV(major, 0));
//	class_destroy(cls);
ERR_STEP2:
	device_destroy(cls, MKDEV(major, 1));
ERR_STEP1:
	class_destroy(cls);
ERR_STEP:
	cdev_del(fsled);
ERR_STEP0:
	unregister_chrdev_region(dev, count);
	return err;


}

static void __exit mydev_exit(void)
{

//	printk("%s , %d\n", __func__, __LINE__);
//	device_destroy(cls, MKDEV(major, 0));
	printk("%s , %d\n", __func__, __LINE__);
	device_destroy(cls, MKDEV(major, 1));
	class_destroy(cls);
	//devm_gpio_free(MKDEV(major_led, 0), gpio);
	cdev_del(fsled);
	unregister_chrdev_region(MKDEV(major, 0),count);
	free_irq(irq, NULL);
	printk("%s , %d\n", __func__, __LINE__);
}
module_init(mydev_init);
module_exit(mydev_exit);
MODULE_LICENSE("GPL");
